package uiLayer;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.util.ArrayList;
import java.util.List;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;

import controlLayer.ScheduleCtr;
import controlLayer.TeachingBlockCtr;
import dbLayer.DBSchedule;
import modelLayer.Block;
import modelLayer.Schedule;
import modelLayer.Class;
import modelLayer.TeachingBlock;
import uiLayer.util.ConflictCheckSupporter;
import uiLayer.util.EditViewTemplate;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

/**
 * ScheduleEditPanel is the right component of the SchedulePanel and registers user input
 * regarding modification of schedules.
 *
 */
public class ScheduleEditPanel extends EditViewTemplate {
	private static final long serialVersionUID = 1L;
	
	private ScheduleCtr scheduleCtr;
	private Schedule sch;
	private Class cl;
	private ScheduleTable scheduleTable;
	private JList<TeachingBlock> teachingBlockList;
	private List<TeachingBlock> tbListOfClass;
	private DefaultListModel<TeachingBlock> tbListModel;
	private ConflictCheckSupporter ccSupporter;
		
	/**
	 * Constructs the panel taking the scheduleCtr and the schedule objects from the parent panel
	 * SchedulePanel. Initiates the instance variable schedule by getting a new schedule object from 
	 * the database (using the value of id of the parameter schedule). The boolean value true in the call
	 * means that associations (blocks) of the schedule are to be built.
	 * Initiates an instance of ConflictCheckSupporter.
	 * Builds up the layout.
	 * 
	 * @param scheduleCtr
	 * @param schedule
	 */
	public ScheduleEditPanel(ScheduleCtr scheduleCtr, Schedule schedule) {
		
		tbListOfClass = new ArrayList<TeachingBlock>();
		this.scheduleCtr = scheduleCtr;
		this.sch = new DBSchedule().findById(schedule.getId(), true);
		this.cl = schedule.getCl();
		this.ccSupporter = new ConflictCheckSupporter(sch);
		
		super.getBtnReset().setText("Tøm skema");
		super.getBtnReset().setToolTipText("Sletter alt indhold i skemaet");
		JPanel centerPanel = super.getCenterPanel();
		centerPanel.setBorder(new TitledBorder(new EmptyBorder(5, 5, 5, 5), cl.getClassName(), TitledBorder.LEADING, TitledBorder.TOP, null, null));
		
		JPanel SidePanel = new JPanel();
		SidePanel.setBorder(new TitledBorder(new EmptyBorder(5, 5, 5, 5), "V\u00E6lg undervisningsblok", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		centerPanel.add(SidePanel, BorderLayout.EAST);
		SidePanel.setLayout(new BorderLayout(0, 0));
		
		JScrollPane listScrollPane = new JScrollPane();
		SidePanel.add(listScrollPane, BorderLayout.CENTER);
		
		tbListModel = new DefaultListModel<TeachingBlock>();
		teachingBlockList = new JList<>(tbListModel);
		initiateTbListOfClass();
		initiateListModel();
		teachingBlockList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
		teachingBlockList.addListSelectionListener(new ListSelectionListener() {
			
			@Override
			public void valueChanged(ListSelectionEvent e) {
				
				setSource(teachingBlockList.getSelectedValue(), teachingBlockList.getSelectedIndex());
			
			}
		});
		
		listScrollPane.setViewportView(teachingBlockList);
		
		JPanel buttonWrapPanel = new JPanel();
		SidePanel.add(buttonWrapPanel, BorderLayout.SOUTH);
		buttonWrapPanel.setLayout(new BorderLayout(0, 0));
		
		JPanel buttonPanelTop = new JPanel();
		buttonPanelTop.setBorder(new EmptyBorder(5, 5, 5, 5));
		FlowLayout fl_buttonPanelTop = (FlowLayout) buttonPanelTop.getLayout();
		fl_buttonPanelTop.setAlignment(FlowLayout.TRAILING);
		buttonWrapPanel.add(buttonPanelTop, BorderLayout.NORTH);
		
		JButton btnTBtoTable = new JButton("<< Tilføj til skema");
		btnTBtoTable.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				try {
					startCheckConflict();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
		btnTBtoTable.setToolTipText("Tilføjer undervisningsblok til skemaet");
		buttonPanelTop.add(btnTBtoTable);
		
		JButton btnTBtoList = new JButton("Put på liste >>");
		btnTBtoList.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				moveTBtoList();
			}
		});
		btnTBtoList.setToolTipText("Sætter undervisningsblock tilbage på listen");
		buttonPanelTop.add(btnTBtoList);
		
		JPanel buttonPanelBottom = new JPanel();
		buttonPanelBottom.setBorder(new EmptyBorder(5, 5, 5, 5));
		FlowLayout flowLayout = (FlowLayout) buttonPanelBottom.getLayout();
		flowLayout.setAlignment(FlowLayout.TRAILING);
		buttonWrapPanel.add(buttonPanelBottom, BorderLayout.SOUTH);
		
		JButton btnReset = new JButton("Nulstil skema");
		btnReset.setToolTipText("Fjerner ændringerne");
		btnReset.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				resetChanges();
			}
		});
		buttonPanelBottom.add(btnReset);
		
		JPanel scheduleTablePanel = new JPanel();
		centerPanel.add(scheduleTablePanel, BorderLayout.CENTER);
		scheduleTablePanel.setLayout(new BorderLayout(0, 0));
		
		JSplitPane splitPane = new JSplitPane();
		splitPane.setOneTouchExpandable(true);
		splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
		scheduleTablePanel.add(splitPane, BorderLayout.CENTER);
		
		JScrollPane tableScrollPane = new JScrollPane();
		tableScrollPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		
		scheduleTable = new ScheduleTable();
		scheduleTable.getTableHeader().setReorderingAllowed(false);
		initiateTableModel();
		BlockColorCellRenderer cellRenderer = new BlockColorCellRenderer();
		cellRenderer.setCcSupporter(this.ccSupporter);
		scheduleTable.setDefaultRenderer(Object.class, cellRenderer);
		
		tableScrollPane.setViewportView(scheduleTable);
		
		scheduleTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			
			@Override
			public void valueChanged(ListSelectionEvent e) {
				setTarget(scheduleTable.getSelectedRow(), scheduleTable.getSelectedColumn());
			}
		});
		
		splitPane.setLeftComponent(tableScrollPane);
		
		JPanel panel = new JPanel();
		panel.setBorder(new TitledBorder(new EmptyBorder(5, 5, 5, 5), "Statistik", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		splitPane.setRightComponent(panel);
		panel.setLayout(new BorderLayout(0, 0));
	}

	/**
	 * Selects a block object from the ScheduleTable registered by the ListSelectionListener of the table.
	 * Takes the coordinates of the cell: the number of the row and the column, the selected cell is placed
	 * in the ScheduleTable.
	 * Sets the block object as targetObject on the ConflictCheckSupporter by sending the object and the
	 * row and column values.
	 * 
	 * @param row
	 * @param col
	 */
	private void setTarget(int row, int col) {
		Block targetBlock = scheduleTable.getSelectedObject();
		//System.out.println("setTarget(): " + targetBlock + ": row: " + row + ",col: " + col);
		ccSupporter.setTarget(row, col, targetBlock);
	}

	/**
	 * Selects a teaching block object from the list of teaching blocks registered by the ListSelectionListener.
	 * Takes the selected object and the value of the list index as parameters.
	 * Sets the teaching block object as sourceObject on the ConflictCheckSupporter by sending the
	 * selected object and the index value.
	 * 
	 * @param selectedValue
	 * @param sourceIndex
	 */
	private void setSource(TeachingBlock selectedValue, int sourceIndex) {
		ccSupporter.setSource(selectedValue, sourceIndex);
		scheduleTable.refresh();
	}

	/**
	 * Resets all the changes of the schedule which are not yet saved in the database.
	 */
	private void resetChanges() {
		Schedule scheduleDBState = scheduleCtr.findScheduleByName(sch.getCl().getClassName(), true);
		List<Block> blocksDBState = scheduleDBState.getBlocks();
		scheduleTable.setModel(blocksDBState);
		
		tbListModel.removeAllElements();
		initiateTbListOfClass();
		initiateListModel();
	}

	/**
	 * Puts the teaching block of the selected block from the schedule back to the list of teaching blocks.
	 * Resets the teaching block of the selected block object as null.
	 */
	private void moveTBtoList() {
		tbListModel.addElement(scheduleTable.getSelectedObject().getTeachingBlock());
		scheduleTable.getSelectedObject().setTeachingBlock(null);
		//scheduleTable.setModel(sch.getBlocks()); 
		scheduleTable.refresh();
	}

	
	/**
	 * Prepares moving the selected teaching block to the schedule. Checks if the targetObject 
	 * is null. If there is a block selected in the scheduleTable, the next step of moving the object 
	 * is called.
	 * 
	 */
	private void startCheckConflict() {
		//System.out.println("source: " + ccSupporter.getSourceObject() + ",target: " + ccSupporter.getTargetCol() + ", " + ccSupporter.getTargetRow());
//		if (!ccSupporter.checkConflict() && ccSupporter.getTargetObject() != null){
//			if (ccSupporter.getTargetObject().getTeachingBlock() != null){
//				tbListModel.addElement(ccSupporter.getTargetObject().getTeachingBlock()); 
//			}
//			ccSupporter.getTargetObject().setTeachingBlock(ccSupporter.getSourceObject());
//			this.tbListModel.remove(ccSupporter.getSourceIndex());
//			scheduleTable.setModel(sch.getBlocks());
//		}
//		else {
//			new StandardDialog().showErrorMessage("Konflikt");
//			scheduleTable.refresh();
//		}
		
		if (ccSupporter.getTargetObject() != null){
			moveTBtoTable();
		}
		else {
			new StandardDialog().noSelectionDialog("blok ");
		}
		
	}

	/**
	 * Checks if moving of a teaching block to a block in the scheduleTabel provokes 
	 * a conflict by calling the checkConflict() method on the ConflictCheckSupporter.
	 * If there is no conflict:
	 * If the targetObject (block) already has a teachingBlock associated than the two teaching blocks 
	 * switch their places.(Old to the list, new selection to the table)
	 * If the targetObject (block) is empty than the new selection (the sourceObject) gets 
	 * associated to the targetObject (block).
	 * If there occurs a conflict, a dialog is showing up. By clicking OK on the dialog box
	 * suggestions of non conflicting places are shown.
	 */
	private void moveTBtoTable() {
		if (!ccSupporter.checkConflict()){
			if (ccSupporter.getTargetObject().getTeachingBlock() != null){
				tbListModel.addElement(ccSupporter.getTargetObject().getTeachingBlock()); 
			}
			ccSupporter.getTargetObject().setTeachingBlock(ccSupporter.getSourceObject());
			this.tbListModel.remove(ccSupporter.getSourceIndex());
			//scheduleTable.setModel(sch.getBlocks());
			scheduleTable.refresh();
		}
		else {
			new StandardDialog().showErrorMessage("Konflikt");
			scheduleTable.refresh();
		}
	}

	/**
	 * Initiates the list of teaching blocks by getting the teaching blocks from the database.
	 * Refreshes the schedule instance of the class in order to get the state, which is responding 
	 * the state found in the database.
	 */
	private void initiateTbListOfClass() {
		try {
			tbListOfClass = new TeachingBlockCtr().getAllByClass(cl.getId(), true);
			refreshSchedule(this.sch);
			List<Block> blocks = sch.getBlocks();
			System.out.println("init tbList: " + blocks);
			
			List<TeachingBlock> scheduledTBList = new ArrayList<>();
			for (Block b : blocks) {
				if (b.getTeachingBlock() != null) {
					scheduledTBList.add(b.getTeachingBlock());
				}
			}
			removeScheduledTBfromList(scheduledTBList);
		}
		catch (Exception e) {
			//TODO
		}
		
	}

	/**
	 * Refreshes the schedule instance of the class in order to get the state, which is responding 
	 * the state found in the database.
	 * @param schedule
	 */
	private void refreshSchedule(Schedule schedule) {
		this.sch = new DBSchedule().findById(schedule.getId(), true);
	}

	/**
	 * Helper method which filters the list of teaching blocks, that the already scheduled teaching blocks are not 
	 * showing in the list but only in the table.
	 * 
	 * @param scheduledTBList
	 */
	private void removeScheduledTBfromList(List<TeachingBlock> scheduledTBList) {
		for (TeachingBlock tb : scheduledTBList) {
			if (tbListOfClass.contains(tb)){
				tbListOfClass.remove(tb);
			}
		}
	}

	/**
	 * Initiates the listModel of the teaching block list.
	 */
	private void initiateListModel() {
		if (tbListOfClass != null) {
			for (TeachingBlock tb : tbListOfClass){
				tbListModel.addElement(tb);
			}
		}
		else {
			new StandardDialog().showErrorMessage("Der findes ikke en liste over undervisningsblokke for " + cl.getClassName());
		}
	}
	
	/**
	 * Initiates the tableModel of the scheduleTable.
	 */
	private void initiateTableModel() {
		List<Block> blocks = sch.getBlocks();
		scheduleTable.setModel(blocks);
	}


	@Override
	protected void doReset() {
		List<Block> blocks = scheduleTable.getDataModel();
		for (Block b : blocks){
			b.setTeachingBlock(null);
		}
		int n = new StandardDialog().deleteDialog("Skema", "skemaet for " + cl.getClassName());
		if (n == JOptionPane.YES_OPTION){
			try {
				scheduleCtr.updateSchedule(sch, blocks);
				resetChanges();
			}
			catch (Exception e){
				//TODO
			}
			
		}
		
	}

	@Override
	protected void doSave() {
		List<Block> blocks = scheduleTable.getDataModel();
		try {
			scheduleCtr.updateSchedule(sch, blocks);
			new StandardDialog().objectSaved("skema ");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	///////////////////////Inner class
	private class BlockColorCellRenderer extends DefaultTableCellRenderer {
		private static final long serialVersionUID = 1L;
		private Color defaultColor = super.getBackground();
		private Color highlightColor = Color.PINK;
		private ConflictCheckSupporter ccSupporter;
		
		@Override
		public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
			//System.out.println("rendererer row: " + row + " col: " + column);
			//return super.getTableCellRendererComponent(table, value, isSelected, hasFocus,row, column);
			Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
			
			//if(row == 0 && column == 0)
			//if(row == column)
			
			if (this.ccSupporter != null && this.ccSupporter.isBlockAvailable(column, row))
				c.setBackground(defaultColor);
			else
				c.setBackground(highlightColor);
			return c;
		}

		/**
		 * @param ccSupporter the ccSupporter to set
		 */
		public void setCcSupporter(ConflictCheckSupporter ccSupporter) {
			//System.out.println("cellRenderer.setCcSupporter() " + ccSupporter);
			this.ccSupporter = ccSupporter;
		}
		
	}
	
}


